import traceback
from abc import abstractmethod, ABC

from ygframe.logger import logger
from ygframe.util.retry import retry

from model.mongo_model.resource_info import ResourceInfo
from util.kube_util.kube_op import create_rc, release_rc
from util.str_util import get_random_kube_name

STATUS = 'status'
CONFIG = 'config'
ACCESS_INFO = 'access_info'


class Resource(ABC):

    def __init__(self, resource_info: ResourceInfo) -> None:
        self.resource_info = resource_info

    @classmethod
    def save_resource_info(cls, info: ResourceInfo):
        # todo times limit
        while True:
            try:
                resource_type = cls.get_resource_type().lower().replace('_', '-')[:5]
                info.resource_id = '{}{}'.format(
                    resource_type, get_random_kube_name(10))
                info.save()
                logger.info('saved {}'.format(info.resource_id))
                break
            except:
                logger.error(traceback.format_exc())
                continue

    @classmethod
    @abstractmethod
    def get_resource_type(cls):
        raise NotImplementedError

    def get_status(self):
        return self.resource_info.status

    @abstractmethod
    def get_config(self):
        raise NotImplementedError

    def get_access_info(self):
        return {
            'ip': self.resource_info.host_ip,
            'port': self.resource_info.host_port,
            **(self.resource_info.env or {}),
        }

    def create(self):
        create_rc(self.resource_info)

    def release(self):
        release_rc(self.resource_info)
        self.after_release_kube()

    @abstractmethod
    def after_release_kube(self):
        raise NotImplementedError

    def get_info_data(self):
        return {
            STATUS: self.get_status(),
            CONFIG: self.get_config(),
            ACCESS_INFO: self.get_access_info()
        }

    @classmethod
    @abstractmethod
    def create_info(cls, resource_data):
        raise NotImplementedError

    @retry(5)
    def start(self):
        self.release()
        self.create()
        if not self.check():
            raise Exception('failed create {}:{}'.format(
                self.resource_info.resource_type,
                self.resource_info.resource_id
            ))

    @abstractmethod
    def check(self) -> bool:
        raise NotImplementedError
